import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.awt.Color;

/**
 * Esta clase representa a un jugador (el que sea)
 * @author Miguel Martinez Aguilar
 * @version 04 de abril de 2013
 */
public abstract class Jugador extends ScrollActor
{
    // Declaracion de variables de instancia
    protected Tablero tablero;// es el tablero del jugador.
    private String nombre;// es el nombre del jugador.
    protected Lanzador lanz;// es el objeto lanzador.
    protected Armeria armeria;// es la armeria del jugador.
    protected boolean turno;// es un boleano que especifica el turno del jugador.
    private boolean soyPrimero;// es un boleano que especifica si este jugador inicia primero.
    private boolean hayBarco;// es un boleano que indica a jugador si encontro un barco.
    private boolean pendiente;// es un boleano que indica al jugador si tiene pendiente destruir un barco.
    private boolean hayRuta;// es un boleano que indica al jugador si tiene una ruta para destruir un barco.
    private int vertical;// es un numero que indica la ruta encontrada para un barco (vertical, horizontal o nula).
    protected int fila;// es la fila de un espacio en la mira.
    protected int col;// es la columna de un espacio en la mira.
    protected int fila2;// es la fila de un posible espacio que defina la orientacion del barco.
    protected int col2;// es la columna de un posible espacio que defina la orientacion del barco.
    protected int numjug;// es el numero de jugador.

    /**
     * Este es el constructor de jugador.
     * @param nombre: el nombre de este jugador (un string).
     */
    public Jugador(String nombre) 
    {
        tablero = new Tablero();
        this.nombre = nombre;
        lanz = null;
        armeria = null;
        this.turno = false;
        this.soyPrimero = false;
        hayBarco = false;
        pendiente = false;
        hayRuta = false;
        vertical = 0;
        fila = col = fila2 = col2 = 0;
    } 
    
    /**
     * Este metodo permite al oponente jugar.
     */
    public void act() 
    {
        if(turno){
            if(!lanz.lanzamiento()){
                if(contador(20))this.buscaTiro();//
            }
            else{
                if(lanz.lanzamiento()){
                    if(lanz.impacto()){
                        if(lanz.hayBarco()){
                            if(numjug > 1)this.descartaDiagonales(fila2,col2);
                            this.continuaTurno(true);
                        }
                        else this.terminaTurno();
                    }
                }
            }
        }
    }
    
    /**
     * Este metodo busca un tiro o tambien trata de rematar un barco.
     */
    public void buscaTiro()
    {
        if(!hayBarco)this.tiroAleatorio();
        else{
            if(pendiente){
                hayRuta = true;
                if(vertical == 1)this.buscaVertical();
                if(vertical == 2)buscaHorizontal();
            }
            else{
                if(hayRuta){
                    if(vertical == 1){
                        this.turnoV();
                        this.buscaVertical();
                    }
                    if(vertical == 2){
                        this.turnoH();
                        this.buscaHorizontal();
                    }
                }
                else this.buscaPosicion();
            }
        }
    }
    
    /**
     * Este metodo crea el tablero de este jugador.
     * @param x: coordenada x donde se colocara el tablero en el escenario.
     * @param y: coordenada y donde se colocara el tablero en el escenario.
     * @param derecha: boleano que establece la orientacion del tablero en el escenario.
     */
    public void creaTablero(int x, int y, boolean derecha) 
    {
        ScrollWorld escena = super.getWorld();
        escena.addObject(tablero, x, y);
        if(derecha)tablero.llenaMatrizR();
        else tablero.llenaMatriz();
    }
    
    /**
     * Este metodo devuelve el valor de turno.
     */
    public boolean turno() 
    {
        return turno;
    }
    
    /**
     * Este metodo permite que el jugador juegue.
     */
    public void juega() 
    {
        turno = true;
        lanz.derecha(!soyPrimero);
    }
    
    /**
     * Este metodo prepara al jugador para el juego.
     * @param lanz: el lanzador que requerira el jugador.
     * @param creador: el objeto CreaFlota que sera el encargado de crear y poner los barcos en el escenario.
     * @param fx: coordenada (x) para colocar al objeto CreaFlota.
     * @param fy: coordenada (y) para colocar al objeto CreaFlota.
     */
    public void preparar(Lanzador lanz, CreaFlota creador, int fx, int fy) 
    {
        this.lanz = lanz;
        this.agregaArmeria(lanz,fx,false);
        ScrollWorld mundo = super.getWorld();
        this.pintaNombre();
        mundo.addObject(creador,fx,fy);
        creador.preparar(true);
    }
    
    /**
     * Este metodo devuelve el tablero.
     */
    public Tablero entregaTablero() 
    {
        return tablero;
    }
    
    /**
     * Este metodo asigna un tablero.
     * @param tab: el tablero que usara el jugador.
     */
    public void tomaTablero(Tablero tab) 
    {
        tablero = tab;
    }
    
    /**
     * Este metodo le dice al jugador que el inicia.
     */
    public void soyPrimero()
    {
        soyPrimero = true;
    }
    
    /**
     * Este metodo termina el turno del jugador.
     */
    public void terminaTurno()
    {
        lanz.lanzamiento(false);
        lanz.impacto(false);
        lanz.cambiaTurno(!soyPrimero);
        pendiente = false;
        turno = false;
    }
    
    /**
     * Este metodo permite a jugador continuar su turno.
     * @param inspeccion: booleano que indica si el jugador descartara algunos espacios.
     */
    public void continuaTurno(boolean inspeccion)
    {
        lanz.lanzamiento(false);
        lanz.impacto(false);
        if(lanz.barcoDestruido()){
            lanz.lanzamiento(true);
            lanz.impacto(true);
            if(lanz.explotaBarco()){
                lanz.barcoDestruido(false);
                hayBarco = false;
                vertical = 0;
                pendiente = false;
                hayRuta = false;
                lanz.lanzamiento(false);
                lanz.impacto(false);
                if(inspeccion)this.descartaEnCruz(fila2,col2);
            }
        }
        else hayBarco = true;
    }
    
    /**
     * Este metodo pide un lanzamiento a Armeria.
     * @param esp: espacio al que va dirigido el ataque.
     * @param f: fila del espacio al que va dirigido el ataque.
     * @param c: columna del espacio al que va dirigido el ataque.
     */
    public void pideLanzamiento(Espacio esp, int f, int c)
    {
        if(esp != null){
            armeria.enviaProyectil(esp);
            tablero.quitaEsp(f,c);
            armeria.lanzamiento(true);
        }
    }
    
    /**
     * Este metodo cambia de direccion la orientacion del ataque del usuario en horizontal.
     */
    public void orientarCol2()
    {
        if(col > col2)col2 = col + 1;
        else col2 = col - 1;
    }
    
    /**
     * Este metodo cambia de direccion la orientacion del ataque del usuario en vertical.
     */
    public void orientarFila2()
    {
        if(fila > fila2)fila2 = fila + 1;
        else fila2 = fila - 1;
    }
    
    /**
     * Este metodo evalua un posible blanco para destruir. <p>
     * Regresa un boleano que indica si hay un espacio con los datos proporcionados.
     * @param f: la fila de un posible espacio a destruir.
     * @param c: la columna de un posible espacio a destruir.
     */
    public boolean evaluaBlanco(int f, int c)
    {
        boolean val = false;
        Espacio esp = null;
        esp = tablero.hayEspacio(f,c);
        if(esp != null){
            this.pideLanzamiento(esp,f,c);
            val = true;
        }
        return val;
    }
    
    /**
     * Este metodo selecciona un proyectil.
     */
    public void queProyectil()
    {
        boolean especial = false;
        int num = Greenfoot.getRandomNumber(10);
        if(num > 7)especial = true;
        armeria.eligeProyectil(especial);
    }
    
    /**
     * Este metodo prepara el proximo tiro vertical si pendiente = true.
     */
    public void turnoV()
    {
        if(fila > fila2){
            fila2 = fila;
            fila--;
        }
        else{
            fila2 = fila;
            fila++;
        }
    }
    
    /**
     * Este metodo prepara el proximo tiro horizontal si pendiente = true.
     */
    public void turnoH()
    {
        if(col > col2){
            col2 = col;
            col--;
        }
        else{
            col2 = col;
            col++;
        }
    }
    
    /**
     * Este metodo busca un tiro en una zona de cruz.
     */
    public void buscaPosicion()
    {
        int fa = fila - 1;
        int fb = fila + 1;
        int ci = col - 1;
        int cd = col + 1;
        int num = 0;
        boolean valido = false;
        do{
            num = Greenfoot.getRandomNumber(4);
            if(num == 0){
                fila2 = fila; 
                col2 = ci;
                vertical = 2;
            }
            if(num == 1){
                fila2 = fila; 
                col2 = cd;
                vertical = 2;
            }
            if(num == 2){
                fila2 = fa; 
                col2 = col;
                vertical = 1;
            }
            if(num == 3){
                fila2 = fb; 
                col2 = col;
                vertical = 1;
            }
            armeria.eligeProyectil(false);
            if(evaluaBlanco(fila2,col2))valido = pendiente = true;
        }while(!valido);
    }
    
    /**
     * Este metodo busca un tiro en una zona en vertical.
     */
    public void buscaVertical()
    {
        boolean valido = false;
        do{
            this.cambiaFila2();
            if(evaluaBlanco(fila2,col))valido = pendiente = true;
            else{
                this.orientarFila2();
                if(evaluaBlanco(fila2,col))valido = pendiente = true;
            }
        }while(!valido);
    }
    
    /**
     * Este metodo busca un tiro en una zona en horizontal.
     */
    public void buscaHorizontal()
    {
        boolean valido = false;
        do{
            this.cambiaCol2();
            if(evaluaBlanco(fila,col2))valido = pendiente = true;
            else{
                this.orientarCol2();
                if(evaluaBlanco(fila,col2))valido = pendiente = true;
            }
        }while(!valido);
    }
    
    /**
     * Este metodo aumenta o disminuye el valor de fila2 con respecto de fila.
     */
    public void cambiaFila2()
    {
        if(fila > fila2)fila2--;
        else fila2++;
    }
    
    /**
     * Este metodo aumenta o disminuye el valor de col2 con respecto de col.
     */
    public void cambiaCol2()
    {
        if(col > col2)col2--;
        else col2++;
    }
    
    /**
     * Este metodo retorna true, que indica que los barcos estaran ocultos.
     */
    public boolean barcosOcultos()
    {
        return true;
    }
    
    /**
     * Este metodo busca y descarta espacios diagonales alrededor de un espacio seleccionado.
     * @param fila: la fila de referencia de un espacio destruido.
     * @param col: la columna de referencia de un espacio destruido.
     */
    public void descartaDiagonales(int fila, int col)
    {
        if(tablero.hayEspacio(fila - 1,col - 1) != null)tablero.quitaEsp( fila - 1, col - 1);
        if(tablero.hayEspacio(fila - 1,col + 1) != null)tablero.quitaEsp( fila - 1, col + 1);
        if(tablero.hayEspacio(fila + 1,col - 1) != null)tablero.quitaEsp( fila + 1, col - 1);
        if(tablero.hayEspacio(fila + 1,col + 1) != null)tablero.quitaEsp( fila + 1, col + 1);
    }
    
    /**
     * Este metodo busca y descarta espacios en cruz alrededor de un espacio seleccionado.
     * @param fila: la fila de referencia de un espacio destruido.
     * @param col: la columna de referencia de un espacio destruido.
     */
    public void descartaEnCruz(int fila, int col)
    {
        if(tablero.hayEspacio(fila,col - 1) != null)tablero.quitaEsp( fila, col - 1);
        if(tablero.hayEspacio(fila,col + 1) != null)tablero.quitaEsp( fila, col + 1);
        if(tablero.hayEspacio(fila - 1,col) != null)tablero.quitaEsp( fila - 1, col);
        if(tablero.hayEspacio(fila + 1,col) != null)tablero.quitaEsp( fila + 1, col);
    }

    /**
     * Este metodo Agrega la armeria.
     * @param lanz: el lanzador que requerira la armeria.
     * @param x: coordenada x de referencia para agregar la armeria en el escenario.
     * @param usado: boleano que indica si la armeria se usara de forma directa.
     */
    public void agregaArmeria(Lanzador lanz, int x, boolean usado)//,602,28);
    {
        armeria = new Armeria(lanz,x > 668,usado);
        ScrollWorld mundo = super.getWorld();
        if(x < 668)mundo.addObject(armeria,734,28);
        else mundo.addObject(armeria,602,28);
        armeria.preparar();
    }
    
    /**
     * Este metodo devuelve el tipo de jugador.
     */
    public int numJugador()
    {
        return numjug;
    }
    
    /**
     * Este metodo devuelve el nombre del jugador.
     */
    public String nombreJugador()
    {
        return nombre;
    }
    
    /**
     * Este metodo pinta el nombre del jugador.
     */
    public void pintaNombre()
    {
        ScrollWorld mundo = super.getWorld();
        ImagenM nom = new ImagenM(new GreenfootImage(this.nombreJugador(),28,Color.YELLOW,Color.RED));
        if(this.getX() < 700)mundo.addObject(nom,this.getX() - 150,this.getY() - 30);
        else mundo.addObject(nom,this.getX() + 150,this.getY() - 30);
    }
    
    /**
     * Este metodo abstracto busca un espacio libre aleatorio.
     */
    public abstract void tiroAleatorio();
}